﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;

namespace lumo.input
{

#if WINDOWS
    [StructLayout(LayoutKind.Sequential)]
    internal struct JOYINFOEX 
    {
        public UInt32 dwSize;
        public UInt32 dwFlags; 
        public UInt32 dwXpos;
        public UInt32 dwYpos;
        public UInt32 dwZpos;
        public UInt32 dwRpos;
        public UInt32 dwUpos;
        public UInt32 dwVpos;
        public UInt32 dwButtons;
        public UInt32 dwButtonNumber;
        public UInt32 dwPOV;
        public UInt32 dwReserved1;
        public UInt32 dwReserved2; 
    };

    [StructLayout(LayoutKind.Sequential)]
    internal struct JOYCAPS
    {
        private const int MAXPNAMELEN = 32;
        private const int MAX_JOYSTICKOEMVXDNAME = 260;
        public UInt16  wMid;
        public UInt16  wPid;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAXPNAMELEN)]
        public string szPname;
        public UInt32 wXmin;
        public UInt32  wXmax;
        public UInt32  wYmin;
        public UInt32  wYmax;
        public UInt32  wZmin;
        public UInt32  wZmax;
        public UInt32  wNumButtons;
        public UInt32  wPeriodMin;
        public UInt32  wPeriodMax;
        public UInt32  wRmin;
        public UInt32  wRmax;
        public UInt32  wUmin;
        public UInt32  wUmax;
        public UInt32  wVmin;
        public UInt32  wVmax;
        public UInt32  wCaps;
        public UInt32  wMaxAxes;
        public UInt32  wNumAxes;
        public UInt32  wMaxButtons;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAXPNAMELEN)]
        public string szRegKey; 
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_JOYSTICKOEMVXDNAME)]
        public string szOEMVxD; 
    };

#endif
    /// <summary>
    /// The jostick class.
    /// </summary>
    public class Joystick : IUpdatableOnce
    {
        /// <summary>
        /// Enumerator for joystick buttons
        /// </summary>
        public enum Button
        {
            ///<summary>Button 0 of the joystick</summary>
            JOY0,

            ///<summary>Button 1 of the joystick</summary>
            JOY1,

            ///<summary>Button 2 of the joystick</summary>
            JOY2,

            ///<summary>Button 3 of the joystick</summary>
            JOY3,

            ///<summary>Button 4 of the joystick</summary>
            JOY4,

            ///<summary>Button 5 of the joystick</summary>
            JOY5,

            ///<summary>Button 6 of the joystick</summary>
            JOY6,

            ///<summary>Button 7 of the joystick</summary>
            JOY7,

            ///<summary>Button 8 of the joystick</summary>
            JOY8,

            ///<summary>Button 9 of the joystick</summary>
            JOY9,

            ///<summary>Button 10 of the joystick</summary>
            JOY10,

            ///<summary>Button 11 of the joystick</summary>
            JOY11,

            ///<summary>Button 12 of the joystick</summary>
            JOY12,

            ///<summary>Button 13 of the joystick</summary>
            JOY13,

            ///<summary>Button 14 of the joystick</summary>
            JOY14,

            ///<summary>Button 15 of the joystick</summary>
            JOY15,

            ///<summary>Button 16 of the joystick</summary>
            JOY16,

            ///<summary>Button 17 of the joystick</summary>
            JOY17,

            ///<summary>Button 18 of the joystick</summary>
            JOY18,

            ///<summary>Button 19 of the joystick</summary>
            JOY19,

            ///<summary>Button 20 of the joystick</summary>
            JOY20,

            ///<summary>Button 21 of the joystick</summary>
            JOY21,

            ///<summary>Button 22 of the joystick</summary>
            JOY22,

            ///<summary>Button 23 of the joystick</summary>
            JOY23,

            ///<summary>Button 24 of the joystick</summary>
            JOY24,

            ///<summary>Button 25 of the joystick</summary>
            JOY25,

            ///<summary>Button 26 of the joystick</summary>
            JOY26,

            ///<summary>Button 27 of the joystick</summary>
            JOY27,

            ///<summary>Button 28 of the joystick</summary>
            JOY28,

            ///<summary>Button 29 of the joystick</summary>
            JOY29,

            ///<summary>Button 30 of the joystick</summary>
            JOY30,

            ///<summary>Button 31 of the joystick</summary>
            JOY31,

            ///<summary>POV Up</summary>
            JOYPOVUp,

            ///<summary>POV Left</summary>
            JOYPOVLeft,

            ///<summary>POV Right</summary>
            JOYPOVRight,

            ///<summary>POV Down</summary>
            JOYPOVDown
        }

        /// <summary>
        /// Enumerator for joystick buttons
        /// </summary>
        public enum Trigger
        {
            ///<summary>X Positive Trigger</summary>
            PositiveX,

            ///<summary>X Negative Trigger</summary>
            NegativeX,

            ///<summary>Y Positive Trigger</summary>
            PositiveY,

            ///<summary>Y Negative Trigger</summary>
            NegativeY,

            ///<summary>Z Positive Trigger</summary>
            PositiveZ,

            ///<summary>Z Negative Trigger</summary>
            NegativeZ,

            ///<summary>R Positive Trigger</summary>
            PositiveR,

            ///<summary>R Negative Trigger</summary>
            NegativeR,

            ///<summary>U Positive Trigger</summary>
            PositiveU,

            ///<summary>U Negative Trigger</summary>
            NegativeU,

            ///<summary>V Positive Trigger</summary>
            PositiveV,

            ///<summary>V Negative Trigger</summary>
            NegativeV
        }

        /// <summary>
        /// Enumerator for joystick buttons
        /// </summary>
        public enum Axis
        {
            ///<summary>X Axis of the joystick</summary>
            X,

            ///<summary>Y Axis of the joystick</summary>
            Y,

            ///<summary>Button 2 of the joystick</summary>
            Z,

            ///<summary>Button 3 of the joystick</summary>
            R,

            ///<summary>Button 4 of the joystick</summary>
            U,

            ///<summary>Button 5 of the joystick</summary>
            V
        }

        /// <summary>
        /// JoystickButton object
        /// </summary>
        abstract class JoystickInputObject : IInputObject
        {
            internal Joystick  JoystickParent;
            internal long      InputID = 0;
            internal InputType InputType = InputType.None;
            private bool LastDown = false;
            private bool CurrentDown = false;
            private float RepeatCount = 0;
            private float _RepeatDelay = Config.DefaultRepeatDelay;
            private float _RepeatTime = Config.DefaultRepeatTime;

            // The propertie for frame ID
            private uint _FrameID = 0;
            /// <summary>Access for FrameID</summary>
            public uint FrameID { get { return _FrameID; } }

            /// <summary>
            /// Constructor for JoystickInputObject.
            /// </summary>
            /// <param name="JoystickParent">The parent Joystick</param>
            public JoystickInputObject(Joystick JoystickParent)
            {
                this.JoystickParent = JoystickParent;
                Repeated = false;
            }

            /// <summary>
            /// Initialize the status.
            /// </summary>
            internal void InitializeStatus()
            {
                CurrentDown = IsDown();
                LastDown = CurrentDown;
            }

            /// <summary>
            /// Check if the button is down.
            /// </summary>
            internal abstract bool IsDown();

            /// <summary>
            /// Update the button.
            /// </summary>
            /// <param name="Game">The game object</param>
            public void Update(LumoComponent Game)
            {
                JoystickParent.Update(Game);
            }

            /// <summary>
            /// Internal update called on Joystick.Update
            /// </summary>
            internal virtual void InternalUpdate(LumoComponent Game)
            {
                // Only called once per frame.
                if (_FrameID == Game.FrameID)
                    return;
                _FrameID = Game.FrameID;


                LastDown = CurrentDown;
                CurrentDown = IsDown();
                RepeatUpdate(Game);
            }

            /// <summary>
            /// Internal Repeat Update
            /// </summary>
            private void RepeatUpdate(LumoComponent Game)
            {
                Repeated = false;
                if (Game == null)
                {
                    Repeated = this.Triggered;
                    return;
                }

                if (CurrentDown)
                {
                    if (!LastDown)
                    {
                        Repeated = true;
                        RepeatCount = _RepeatDelay;
                    }
                    else
                    {
                        RepeatCount -= (float)Game.GameTime.ElapsedGameTime.TotalMilliseconds;
                        if (RepeatCount <= 0)
                        {
                            Repeated = true;
                            RepeatCount = _RepeatTime;
                        }
                    }
                }
            }

            /// <summary>
            /// Type of object.
            /// </summary>
            /// <returns>The type of object.</returns>
            public InputType GetInputType() { return this.InputType; }

            /// <summary>
            /// Return the Button.
            /// </summary>
            /// <returns>The ID.</returns>
            public long GetInputID() { return this.InputID; }

            /// <summary>
            /// Property to check if key is pressed.
            /// </summary>
            public bool Pressed { get { return CurrentDown; } }

            /// <summary>
            /// Property to check if key is triggered.
            /// </summary>
            public bool Triggered { get { return (CurrentDown && !LastDown); } }

            /// <summary>
            /// Property to check if key is repeated.
            /// </summary>
            public bool Repeated { get; private set; }

            /// <summary>
            /// Property to check if key is released.
            /// </summary>
            public bool Released { get { return (LastDown && !CurrentDown); } }

            /// <summary>
            /// Implemented IInputObject Released.
            /// </summary>
            /// <returns>True if the key is released.</returns>
            public float RepeatDelay { get { return _RepeatDelay; } set { _RepeatDelay = value; } }

            /// <summary>
            /// Implemented IInputObject Released.
            /// </summary>
            /// <returns>True if the key is released.</returns>
            public float RepeatTime { get { return _RepeatTime; } set { _RepeatTime = value; } }
        }

        /// <summary>
        /// JoystickButton object
        /// </summary>
        class JoystickButton : JoystickInputObject
        {
            private UInt32 ButtonFlag;

            /// <summary>
            /// Constructor for key.
            /// </summary>
            /// <param name="JoystickParent">The parent Joystick</param>
            /// <param name="JoyButton">The JoyButton</param>
            /// <param name="Button">The Button</param>
            public JoystickButton(Joystick JoystickParent, Button JoyButton, int Button)  : base(JoystickParent)
            {
                this.ButtonFlag = (UInt32)(1 << Button);
                this.InputType  = InputType.JoyButton;
                this.InputID    = (long) JoyButton;
                InitializeStatus();
            }

            /// <summary>
            /// 
            /// </summary>
            /// <returns></returns>
            internal override bool IsDown() 
            { 
                #if WINDOWS
                    return ((JoystickParent._JoyInfoEx.dwButtons & this.ButtonFlag) != 0);
                #else
                    return false;
                #endif
            }
        }

        /// <summary>
        /// JoystickButton object
        /// </summary>
        class JoystickPOV : JoystickInputObject
        {
            private Func<UInt32, bool> Function;

            /// <summary>
            /// Constructor for key.
            /// </summary>
            /// <param name="JoystickParent">The parent Joystick</param>
            /// <param name="JoyButton">The JoyButton</param>
            /// <param name="Function">Function to check a POV</param>
            public JoystickPOV(Joystick JoystickParent, Button JoyButton, Func<UInt32, bool> Function)
                : base(JoystickParent)
            {
                this.Function = Function;
                this.InputType = InputType.JoyButton;
                this.InputID = (long)JoyButton;
                InitializeStatus();
            }

            /// <summary>
            /// Check if is down.
            /// </summary>
            /// <returns></returns>
            internal override bool IsDown() 
            { 
                #if WINDOWS
                    return Function(this.JoystickParent._JoyInfoEx.dwPOV); 
                #else
                    return false;
                #endif
            }
        }

        /// <summary>
        /// JoystickTrigger Object
        /// </summary>
        class JoystickTrigger : JoystickInputObject, ITrigger
        {
            private Func<Joystick, float> Function;

            /// <summary>
            /// Get, set the value
            /// </summary>
            public float Value { get; private set; }

            /// <summary>
            /// Constructor for key.
            /// </summary>
            /// <param name="JoystickParent">The parent Joystick</param>
            /// <param name="JoyTrigger">The JoyTrigger</param>
            /// <param name="Function">Function to check an Trigger</param>
            public JoystickTrigger(Joystick JoystickParent, Trigger JoyTrigger, Func<Joystick, float> Function)
                : base(JoystickParent)
            {
                this.Function = Function;
                this.InputType = InputType.JoyTrigger;
                this.InputID = (long)JoyTrigger;
                this.Value = Function(this.JoystickParent);
                InitializeStatus();
            }

            /// <summary>
            /// Check if is down.
            /// </summary>
            /// <returns>True if it is down</returns>
            internal override bool IsDown() 
            { 
                #if WINDOWS
                    return (Math.Abs(this.Value) >= 0.5f); 
                #else
                    return false;
                #endif
            }

            /// <summary>
            /// Internal update called on Gamepad.Update
            /// </summary>
            internal override void InternalUpdate(LumoComponent Game)
            {
                this.Value = Function(this.JoystickParent);
                base.InternalUpdate(Game);
            }
        }

        /// <summary>
        /// JoystickAxis Object
        /// </summary>
        class JoystickAxis : JoystickInputObject, IAxis
        {
            private Func<Joystick, float> Function;
            private float _Value = 0;

            /// <summary>
            /// Access the value
            /// </summary>
            public float Value { get { return (this.Reverse ? -_Value : _Value); } }

            /// <summary>
            /// Reverse the axis
            /// </summary>
            public bool Reverse { get; set; }

            /// <summary>
            /// Constructor for key.
            /// </summary>
            /// <param name="JoystickParent">The parent Joystick</param>
            /// <param name="JoyAxis">The JoyAxis</param>
            /// <param name="Function">Function to check an Trigger</param>
            public JoystickAxis(Joystick JoystickParent, Axis JoyAxis, Func<Joystick, float> Function)
                : base(JoystickParent)
            {
                this.Function = Function;
                this.InputType = InputType.JoyAxis;
                this.InputID = (long)JoyAxis;
                this._Value = Function(this.JoystickParent);
                this.Reverse = false;
                InitializeStatus();
            }

            /// <summary>
            /// Check if is down.
            /// </summary>
            /// <returns>True if it is down</returns>
            internal override bool IsDown() 
            { 
                #if WINDOWS
                    return (Math.Abs(this.Value) >= 0.5f); 
                #else
                    return false;
                #endif
            }

            /// <summary>
            /// Internal update called on Gamepad.Update
            /// </summary>
            internal override void InternalUpdate(LumoComponent Game)
            {
                this._Value = Function(this.JoystickParent);
                base.InternalUpdate(Game);
            }
        }

        // DLL Declarations
        #if WINDOWS
            [DllImport("winmm.dll")]
            internal static extern UInt32 joyGetPosEx(UInt32 uJoyID, ref JOYINFOEX pji);
            [DllImport("winmm.dll")]
            internal static extern UInt32 joyGetDevCaps(UInt32 uJoyID, ref JOYCAPS pji, int cbkc);
            private JOYCAPS _JoyCaps;
            private JOYINFOEX _JoyInfoEx;
        #endif

        private UInt32 _JoyID;
        private Dictionary<Button, JoystickInputObject> JoystickButtonBaseDictionary = new Dictionary<Button, JoystickInputObject>();
        private Dictionary<Trigger, JoystickTrigger> JoystickTriggerDictionary = new Dictionary<Trigger, JoystickTrigger>();
        private Dictionary<Axis, JoystickAxis> JoystickAxisDictionary = new Dictionary<Axis, JoystickAxis>();

        // The propertie for frame ID
        private uint _FrameID = 0;
        /// <summary>Access for FrameID</summary>
        public uint FrameID { get { return _FrameID; } }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="JoyID">The JoystickID</param>
        public Joystick(UInt32 JoyID)
        {
            // Check Joystick
            _JoyID = JoyID;

            // Create jostick buttons
            JoystickButtonBaseDictionary.Add(Button.JOY0, new JoystickButton(this, Button.JOY0, 0));
            JoystickButtonBaseDictionary.Add(Button.JOY1, new JoystickButton(this, Button.JOY1, 1));
            JoystickButtonBaseDictionary.Add(Button.JOY2, new JoystickButton(this, Button.JOY2, 2));
            JoystickButtonBaseDictionary.Add(Button.JOY3, new JoystickButton(this, Button.JOY3, 3));
            JoystickButtonBaseDictionary.Add(Button.JOY4, new JoystickButton(this, Button.JOY4, 4));
            JoystickButtonBaseDictionary.Add(Button.JOY5, new JoystickButton(this, Button.JOY5, 5));
            JoystickButtonBaseDictionary.Add(Button.JOY6, new JoystickButton(this, Button.JOY6, 6));
            JoystickButtonBaseDictionary.Add(Button.JOY7, new JoystickButton(this, Button.JOY7, 7));
            JoystickButtonBaseDictionary.Add(Button.JOY8, new JoystickButton(this, Button.JOY8, 8));
            JoystickButtonBaseDictionary.Add(Button.JOY9, new JoystickButton(this, Button.JOY9, 9));
            JoystickButtonBaseDictionary.Add(Button.JOY10, new JoystickButton(this, Button.JOY10, 10));
            JoystickButtonBaseDictionary.Add(Button.JOY11, new JoystickButton(this, Button.JOY11, 11));
            JoystickButtonBaseDictionary.Add(Button.JOY12, new JoystickButton(this, Button.JOY12, 12));
            JoystickButtonBaseDictionary.Add(Button.JOY13, new JoystickButton(this, Button.JOY13, 13));
            JoystickButtonBaseDictionary.Add(Button.JOY14, new JoystickButton(this, Button.JOY14, 14));
            JoystickButtonBaseDictionary.Add(Button.JOY15, new JoystickButton(this, Button.JOY15, 15));
            JoystickButtonBaseDictionary.Add(Button.JOY16, new JoystickButton(this, Button.JOY16, 16));
            JoystickButtonBaseDictionary.Add(Button.JOY17, new JoystickButton(this, Button.JOY17, 17));
            JoystickButtonBaseDictionary.Add(Button.JOY18, new JoystickButton(this, Button.JOY18, 18));
            JoystickButtonBaseDictionary.Add(Button.JOY19, new JoystickButton(this, Button.JOY19, 19));
            JoystickButtonBaseDictionary.Add(Button.JOY20, new JoystickButton(this, Button.JOY20, 20));
            JoystickButtonBaseDictionary.Add(Button.JOY21, new JoystickButton(this, Button.JOY21, 21));
            JoystickButtonBaseDictionary.Add(Button.JOY22, new JoystickButton(this, Button.JOY22, 22));
            JoystickButtonBaseDictionary.Add(Button.JOY23, new JoystickButton(this, Button.JOY23, 23));
            JoystickButtonBaseDictionary.Add(Button.JOY24, new JoystickButton(this, Button.JOY24, 24));
            JoystickButtonBaseDictionary.Add(Button.JOY25, new JoystickButton(this, Button.JOY25, 25));
            JoystickButtonBaseDictionary.Add(Button.JOY26, new JoystickButton(this, Button.JOY26, 26));
            JoystickButtonBaseDictionary.Add(Button.JOY27, new JoystickButton(this, Button.JOY27, 27));
            JoystickButtonBaseDictionary.Add(Button.JOY28, new JoystickButton(this, Button.JOY28, 28));
            JoystickButtonBaseDictionary.Add(Button.JOY29, new JoystickButton(this, Button.JOY29, 29));
            JoystickButtonBaseDictionary.Add(Button.JOY30, new JoystickButton(this, Button.JOY30, 30));
            JoystickButtonBaseDictionary.Add(Button.JOY31, new JoystickButton(this, Button.JOY31, 31));

            // Add POV
            JoystickButtonBaseDictionary.Add(Button.JOYPOVUp, new JoystickPOV(this, Button.JOYPOVUp, pov => ((pov >= 31500 && pov <= 36000) || pov <= 4500)));
            JoystickButtonBaseDictionary.Add(Button.JOYPOVRight, new JoystickPOV(this, Button.JOYPOVRight, pov => (pov >= 4500 && pov <= 13500)));
            JoystickButtonBaseDictionary.Add(Button.JOYPOVDown, new JoystickPOV(this, Button.JOYPOVDown, pov => (pov >= 13500 && pov <= 22500)));
            JoystickButtonBaseDictionary.Add(Button.JOYPOVLeft, new JoystickPOV(this, Button.JOYPOVLeft, pov => (pov >= 22500 && pov <= 31500)));

            // Add Triggers
            JoystickTriggerDictionary.Add(Trigger.PositiveX, new JoystickTrigger(this, Trigger.PositiveX, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwXpos, self._JoyCaps.wXmin, self._JoyCaps.wXmax), 0, 1))));
            JoystickTriggerDictionary.Add(Trigger.NegativeX, new JoystickTrigger(this, Trigger.NegativeX, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwXpos, self._JoyCaps.wXmin, self._JoyCaps.wXmax), -1, 0))));
            JoystickTriggerDictionary.Add(Trigger.PositiveY, new JoystickTrigger(this, Trigger.PositiveY, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwYpos, self._JoyCaps.wYmin, self._JoyCaps.wYmax), 0, 1))));
            JoystickTriggerDictionary.Add(Trigger.NegativeY, new JoystickTrigger(this, Trigger.NegativeY, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwYpos, self._JoyCaps.wYmin, self._JoyCaps.wYmax), -1, 0))));
            JoystickTriggerDictionary.Add(Trigger.PositiveZ, new JoystickTrigger(this, Trigger.PositiveZ, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwZpos, self._JoyCaps.wZmin, self._JoyCaps.wZmax), 0, 1))));
            JoystickTriggerDictionary.Add(Trigger.NegativeZ, new JoystickTrigger(this, Trigger.NegativeZ, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwZpos, self._JoyCaps.wZmin, self._JoyCaps.wZmax), -1, 0))));
            JoystickTriggerDictionary.Add(Trigger.PositiveR, new JoystickTrigger(this, Trigger.PositiveR, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwRpos, self._JoyCaps.wRmin, self._JoyCaps.wRmax), 0, 1))));
            JoystickTriggerDictionary.Add(Trigger.NegativeR, new JoystickTrigger(this, Trigger.NegativeR, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwRpos, self._JoyCaps.wRmin, self._JoyCaps.wRmax), -1, 0))));
            JoystickTriggerDictionary.Add(Trigger.PositiveU, new JoystickTrigger(this, Trigger.PositiveU, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwUpos, self._JoyCaps.wUmin, self._JoyCaps.wUmax), 0, 1))));
            JoystickTriggerDictionary.Add(Trigger.NegativeU, new JoystickTrigger(this, Trigger.NegativeU, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwUpos, self._JoyCaps.wUmin, self._JoyCaps.wUmax), -1, 0))));
            JoystickTriggerDictionary.Add(Trigger.PositiveV, new JoystickTrigger(this, Trigger.PositiveV, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwVpos, self._JoyCaps.wVmin, self._JoyCaps.wVmax), 0, 1))));
            JoystickTriggerDictionary.Add(Trigger.NegativeV, new JoystickTrigger(this, Trigger.NegativeV, self => (MathHelper.Clamp(ClampRange(self._JoyInfoEx.dwVpos, self._JoyCaps.wVmin, self._JoyCaps.wVmax), -1, 0))));

            // Add Axes
            JoystickAxisDictionary.Add(Axis.X, new JoystickAxis(this, Axis.X, self => ClampRange(self._JoyInfoEx.dwXpos, self._JoyCaps.wXmin, self._JoyCaps.wXmax)));
            JoystickAxisDictionary.Add(Axis.Y, new JoystickAxis(this, Axis.Y, self => ClampRange(self._JoyInfoEx.dwYpos, self._JoyCaps.wYmin, self._JoyCaps.wYmax)));
            JoystickAxisDictionary.Add(Axis.Z, new JoystickAxis(this, Axis.Z, self => ClampRange(self._JoyInfoEx.dwZpos, self._JoyCaps.wZmin, self._JoyCaps.wZmax)));
            JoystickAxisDictionary.Add(Axis.R, new JoystickAxis(this, Axis.R, self => ClampRange(self._JoyInfoEx.dwRpos, self._JoyCaps.wRmin, self._JoyCaps.wRmax)));
            JoystickAxisDictionary.Add(Axis.U, new JoystickAxis(this, Axis.U, self => ClampRange(self._JoyInfoEx.dwUpos, self._JoyCaps.wUmin, self._JoyCaps.wUmax)));
            JoystickAxisDictionary.Add(Axis.V, new JoystickAxis(this, Axis.V, self => ClampRange(self._JoyInfoEx.dwVpos, self._JoyCaps.wVmin, self._JoyCaps.wVmax)));
        }

        /// <summary>
        /// Simple class to clamp value in a range.
        /// </summary>
        /// <param name="Value"></param>
        /// <param name="Min"></param>
        /// <param name="Max"></param>
        /// <returns></returns>
        internal static float ClampRange(float Value, float Min, float Max)
        {
            return MathHelper.Clamp((Value - Min) * 2.0f / (Max - Min) - 1.0f, -1, 1);
        }

        /// <summary>
        /// Update the status of the controller.
        /// </summary>
        /// <returns></returns>
        private bool _UpdateJoystickWinMM()
        {
            #if WINDOWS
                if (joyGetDevCaps(_JoyID, ref _JoyCaps, Marshal.SizeOf(typeof(JOYCAPS))) != 0)
                    return false;
                _JoyInfoEx.dwSize = (UInt32)Marshal.SizeOf(typeof(JOYINFOEX));
                _JoyInfoEx.dwFlags = 255;
                if (joyGetPosEx(_JoyID, ref _JoyInfoEx) != 0)
                    return false;
                return true;
            #else
                return false;
            #endif
        }


        /// <summary>
        /// Update the status of the controller.
        /// </summary>
        /// <returns></returns>
        private void UpdateStatus()
        {
            if (!_UpdateJoystickWinMM())
                ClearStruct();
        }

        /// <summary>
        /// Clear the struct
        /// </summary>
        private void ClearStruct()
        {
            // Clear the struct
            #if WINDOWS
                _JoyInfoEx.dwSize = 0;
                _JoyInfoEx.dwFlags = 0;
                _JoyInfoEx.dwXpos = 1;
                _JoyInfoEx.dwYpos = 1;
                _JoyInfoEx.dwZpos = 1;
                _JoyInfoEx.dwRpos = 1;
                _JoyInfoEx.dwUpos = 1;
                _JoyInfoEx.dwVpos = 1;
                _JoyInfoEx.dwButtons = 0;
                _JoyInfoEx.dwButtonNumber = 0;
                _JoyInfoEx.dwPOV = 0;
                _JoyInfoEx.dwReserved1 = 0;
                _JoyInfoEx.dwReserved2 = 0; 
                _JoyCaps.wXmax = 2;
                _JoyCaps.wXmin = 0;
                _JoyCaps.wYmax = 2;
                _JoyCaps.wYmin = 0;
                _JoyCaps.wZmax = 2;
                _JoyCaps.wZmin = 0;
                _JoyCaps.wRmax = 2;
                _JoyCaps.wRmin = 0;
                _JoyCaps.wUmax = 2;
                _JoyCaps.wUmin = 0;
                _JoyCaps.wVmax = 2;
                _JoyCaps.wVmin = 0;
            #endif
        }

        /// <summary>
        /// Update the joystick
        /// </summary>
        /// <param name="Game">The game object.</param>
        public void Update(LumoComponent Game)
        {
            // Only called once per frame.
            if (_FrameID == Game.FrameID)
                return;
            _FrameID = Game.FrameID;

            UpdateStatus();
            foreach (JoystickInputObject obj in JoystickButtonBaseDictionary.Values)
                obj.InternalUpdate(Game);
            foreach (JoystickTrigger obj in JoystickTriggerDictionary.Values)
                obj.InternalUpdate(Game);
            foreach (JoystickAxis obj in JoystickAxisDictionary.Values)
                obj.InternalUpdate(Game);
        }

        /// <summary>
        /// Get a button of the joystick.
        /// </summary>
        /// <param name="Button">The button to get</param>
        /// <returns>The button</returns>
        public IInputObject GetButton(Button Button)
        {
            return JoystickButtonBaseDictionary[Button];
        }

        /// <summary>
        /// Get all buttons of the joystick.
        /// </summary>
        /// <returns>All joystick buttons</returns>
        public IEnumerable<IInputObject> GetButtons()
        {
            return JoystickButtonBaseDictionary.Values;
        }

        /// <summary>
        /// Get a Trigger of the joystick.
        /// </summary>
        /// <param name="Trigger">The Trigger to get</param>
        /// <returns>The Trigger</returns>
        public ITrigger GetTrigger(Trigger Trigger)
        {
            return JoystickTriggerDictionary[Trigger];
        }

        /// <summary>
        /// Get all Triggers of the joystick.
        /// </summary>
        /// <returns>All joystick Triggers</returns>
        public IEnumerable<ITrigger> GetTriggers()
        {
            return JoystickTriggerDictionary.Values;
        }

        /// <summary>
        /// Get an Axis of the joystick.
        /// </summary>
        /// <param name="Axis">The Axis to get</param>
        /// <returns>The Axis</returns>
        public IAxis GetAxis(Axis Axis)
        {
            return JoystickAxisDictionary[Axis];
        }

        /// <summary>
        /// Get all Axes of the joystick.
        /// </summary>
        /// <returns>All joystick Axes</returns>
        public IEnumerable<IAxis> GetAxes()
        {
            return JoystickAxisDictionary.Values;
        }
    }
}
